package dao

import (
	"github.com/astaxie/beego/orm"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/model"
	"nft-server/util"
	"time"
)

type CollectionDetailDao struct {
	BaseDao
}

type ICollectionDetailDao interface {
	SaveCollectionDetailInfo(collectionDetail *model.CollectionDetail) error
	UpdateCollectionDetailInfo(collectionDetail *model.CollectionDetail) error
	UpdateAllSubCollectionDetailInfo(modifyParameter *common.ModifyCollectionDetail) error
	UpdateCollectionDetailStatusAndTokenId(userId int, collectionIndex string, collectionStatus, startTokenId *int) error
	GetCollectionDetailByUserIdAndCollection(userId int, collectionIndex string, subCollectionIndex int) (*model.CollectionDetail, error)
	GetLastCollectionByTime(userId int, collectionStatus []int, nowTime time.Time, num int) (collections []*model.CollectionDetail, collectionSize, totalNum int, err error)
	GetCollectionByNameExact(collectionName string) ([]*model.CollectionDetail, error)
	DeleteCollectionDetail(collectionId int) error
	GetCollection(queryParameter *common.QueryCollectionDetail) (collection []*model.CollectionDetail, collectionSize, totalNum int, err error)
}

func GetCollectionDetailDao() ICollectionDetailDao {
	dao := &CollectionDetailDao{}
	dao.Init()
	return dao
}
func buildCollectionStatusCondition(queryParameter *common.QueryCollectionDetail, finalCondition *orm.Condition) *orm.Condition {
	if len(queryParameter.CollectionStatus) > 1 {
		finalCondition = finalCondition.And("collection_status__in", queryParameter.CollectionStatus)
	} else {
		collectionStatus := queryParameter.CollectionStatus[0]
		if util.InArray(collectionStatus, util.CollectionStatusForQueryDirect) {
			finalCondition = finalCondition.And("collection_status", collectionStatus)
		} else {
			if collectionStatus != util.DefaultCollectionStatus {
				finalCondition = finalCondition.And("collection_status", util.CollectionPublished)
				if queryParameter.NowTime != nil {
					nowTime := *queryParameter.NowTime
					condition := orm.NewCondition()
					timeCondition := orm.NewCondition()
					publishTime := condition.And("sale_time__gt", nowTime)
					saleTime := condition.And("sale_time__lt", nowTime).And("off_shelf_time__gt", nowTime)
					endTime := condition.And("off_shelf_time__lt", nowTime)
					if collectionStatus == util.CollectionPublished {
						//已发布（已发布未出售）
						timeCondition = condition.AndCond(publishTime)
					} else if collectionStatus == util.CollectionOnSale {
						//出售中
						timeCondition = condition.AndCond(saleTime)
					} else if collectionStatus == util.CollectionEnded {
						//已结束
						timeCondition = condition.AndCond(endTime)
					}
					if timeCondition.IsEmpty() == false {
						finalCondition = finalCondition.AndCond(timeCondition)
					}
				}
			}
		}
	}
	return finalCondition
}

//考虑分页,找不到,模糊查询的情况
func buildConditionForGetCollection(queryParameter *common.QueryCollectionDetail, finalCondition *orm.Condition) *orm.Condition {
	if queryParameter.UserId != nil {
		finalCondition = finalCondition.And("user_id", *queryParameter.UserId)
	}
	if queryParameter.CollectionIndex != nil {
		finalCondition = finalCondition.And("collection_index__in", queryParameter.CollectionIndex)
	}
	if queryParameter.SubCollectionIndex != nil {
		finalCondition = finalCondition.And("sub_collection_index", *queryParameter.SubCollectionIndex)
	}
	if queryParameter.NeedMainCollection != nil {
		needMainCollection := *queryParameter.NeedMainCollection
		if needMainCollection == false {
			finalCondition = finalCondition.AndNot("sub_collection_index", util.DefaultSubCollectionIndex)
		}
	}
	nameCondition := orm.NewCondition()
	if queryParameter.CollectionName != nil {
		nameCondition = nameCondition.And("collection_name__contains", *queryParameter.CollectionName)
	}
	if queryParameter.Issuer != nil {
		if nameCondition.IsEmpty() == false {
			nameCondition = nameCondition.Or("issuer__contains", *queryParameter.Issuer)
		} else {
			nameCondition = nameCondition.And("issuer__contains", *queryParameter.Issuer)
		}
	}
	if nameCondition.IsEmpty() == false {
		finalCondition = finalCondition.AndCond(nameCondition)
	}
	if queryParameter.CollectionStatus != nil {
		finalCondition = buildCollectionStatusCondition(queryParameter, finalCondition)
	}
	return finalCondition
}
func (cdd *CollectionDetailDao) GetCollection(queryParameter *common.QueryCollectionDetail) (collection []*model.CollectionDetail, collectionSize, totalNum int, err error) {
	var collections []*model.CollectionDetail
	//通过查询参数设定查询条件
	finalCondition := orm.NewCondition()
	if queryParameter == nil {
		return nil, util.InvalidNumber, util.InvalidNumber, errors.Errorf("query parameter can't be nil")
	}
	finalCondition = buildConditionForGetCollection(queryParameter, finalCondition)
	recode := cdd.O.QueryTable(&model.CollectionDetail{})
	recode = recode.SetCond(finalCondition)
	arrayNum, err := recode.Count()
	//分页查询
	if queryParameter.Page != nil && queryParameter.PageSize != nil {
		page := *queryParameter.Page
		pageSize := *queryParameter.PageSize
		offset := page - 1
		if page-1 < 0 {
			offset = 0
		}
		offset = offset * pageSize
		recode = recode.Offset(offset).Limit(pageSize)
	}
	//去重
	if queryParameter.Distinct != nil {
		if *queryParameter.Distinct == true {
			recode = recode.Distinct()
		}
	}
	_, err = recode.All(&collections)
	if err != nil {
		if err == orm.ErrNoRows {
			return nil, util.InvalidNumber, util.InvalidNumber, nil
		}
		return nil, util.InvalidNumber, util.InvalidNumber, errors.Errorf("read error: %v", err)
	}
	collectionSize = len(collections)
	totalNum = cast.ToInt(arrayNum)
	return collections, collectionSize, totalNum, nil
}

func (cdd *CollectionDetailDao) GetCollectionByNameExact(collectionName string) ([]*model.CollectionDetail, error) {
	var collections []*model.CollectionDetail
	if len(collectionName) == 0 {
		return nil, nil
	}
	_, err := cdd.O.QueryTable(&model.CollectionDetail{}).Filter("collection_name", collectionName).All(&collections)
	if err != nil {
		return nil, err
	}
	return collections, nil
}

func (cdd *CollectionDetailDao) GetSubCollectionByCollectionIndex(userId int, collectionIndex []string) ([]*model.CollectionDetail, error) {
	var collectionDetails []*model.CollectionDetail
	recode := cdd.O.QueryTable(&model.CollectionDetail{})
	condition := orm.NewCondition()
	userCondition := orm.NewCondition()
	identityCondition := orm.NewCondition()
	finalCondition := orm.NewCondition()
	if userId != util.InvalidNumber {
		userCondition = condition.And("user_id", userId)
	}
	if len(collectionIndex) > 0 {
		identityCondition = condition.And("collection_index__in", collectionIndex)
	}
	if identityCondition.IsEmpty() == false {
		finalCondition = finalCondition.AndCond(identityCondition)
	}
	if userCondition.IsEmpty() == false {
		finalCondition = finalCondition.AndCond(userCondition)
	}
	if finalCondition.IsEmpty() == false {
		recode = recode.SetCond(finalCondition)
		_, err := recode.All(&collectionDetails)
		if err != nil {
			if err == orm.ErrNoRows {
				return nil, nil
			}
			return nil, errors.Errorf("read error: %v", err)
		}
	}
	return collectionDetails, nil
}

func (cdd *CollectionDetailDao) SaveCollectionDetailInfo(collectionDetail *model.CollectionDetail) error {
	_, err := cdd.O.Insert(collectionDetail)
	if err != nil {
		return errors.Errorf("insert error: %v", err)
	}
	return nil
}

func (cdd *CollectionDetailDao) GetCollectionDetailByUserIdAndCollection(userId int, collectionIndex string, subCollectionIndex int) (*model.CollectionDetail, error) {
	collectionDetail := model.CollectionDetail{}
	recode := cdd.O.QueryTable(&model.CollectionDetail{})
	condition := orm.NewCondition()
	userCondition := orm.NewCondition()
	identityCondition := condition.And("collection_index", collectionIndex).And("sub_collection_index", subCollectionIndex)
	if userId != util.InvalidNumber {
		userCondition = condition.And("user_id", userId)
	}
	finalCondition := condition.AndCond(identityCondition)
	if userCondition.IsEmpty() == false {
		finalCondition = finalCondition.AndCond(userCondition)
	}
	recode = recode.SetCond(finalCondition)
	err := recode.One(&collectionDetail)
	if err != nil {
		if err == orm.ErrNoRows {
			return nil, nil
		}
		return nil, errors.Errorf("read error: %v", err)
	}
	return &collectionDetail, nil
}
func (cdd *CollectionDetailDao) UpdateCollectionDetailInfo(collectionDetail *model.CollectionDetail) error {
	_, err := cdd.O.Update(collectionDetail)
	if err != nil {
		return errors.Errorf("update error: %v", err)
	}
	return nil
}
func (cdd *CollectionDetailDao) UpdateCollectionDetailStatusAndTokenId(userId int, collectionIndex string, collectionStatus, startTokenId *int) error {
	recode := cdd.O.QueryTable(&model.CollectionDetail{}).Filter("user_id", userId).Filter("collection_index", collectionIndex)
	if collectionStatus != nil {
		_, err := recode.Update(orm.Params{
			"collection_status": collectionStatus,
		})
		if err != nil {
			return errors.Errorf("update error: %v", err)
		}
	}
	if startTokenId != nil {
		_, err := recode.Update(orm.Params{
			"token_id": startTokenId,
		})
		if err != nil {
			return errors.Errorf("update error: %v", err)
		}
	}
	return nil
}

func (cdd *CollectionDetailDao) UpdateAllSubCollectionDetailInfo(modifyParameter *common.ModifyCollectionDetail) error {
	modify := orm.Params{}
	if modifyParameter.CollectionName != nil {
		modify["collection_name"] = *modifyParameter.CollectionName
	}
	if modifyParameter.SaleTime != nil {
		modify["sale_time"] = *modifyParameter.SaleTime
	}
	if modifyParameter.OffShelfTime != nil {
		modify["off_shelf_time"] = *modifyParameter.OffShelfTime
	}
	if modifyParameter.Price != nil {
		modify["price"] = *modifyParameter.Price
	}
	if modifyParameter.AfterCollectionStatus != nil {
		modify["collection_status"] = *modifyParameter.AfterCollectionStatus
	}
	if len(modify) != 0 {
		_, err := cdd.O.QueryTable(&model.CollectionDetail{}).Filter("user_id", modifyParameter.UserId).Filter("collection_index", modifyParameter.CollectionIndex).Filter("collection_status__in", modifyParameter.QueryCollectionStatus).Update(modify)
		if err != nil {
			return errors.Errorf("update error: %v", err)
		}
	}
	return nil
}
func (cdd *CollectionDetailDao) GetLastCollectionByTime(userId int, collectionStatus []int, nowTime time.Time, num int) (collections []*model.CollectionDetail, collectionSize, totalNum int, err error) {
	var collectionDetails []*model.CollectionDetail
	recode := cdd.O.QueryTable(&model.CollectionDetail{})
	//筛选条件

	condition := orm.NewCondition()
	identityCondition := condition.And("user_id", userId).And("collection_status__in", collectionStatus).AndNot("sub_collection_index", util.DefaultSubCollectionIndex)
	//增加筛选--出售中和已发布未出售

	saleTime := condition.And("sale_time__lt", nowTime).And("off_shelf_time__gt", nowTime)
	publishTime := condition.And("sale_time__gt", nowTime)
	timeCondition := condition.AndCond(saleTime).OrCond(publishTime)
	finalCondition := condition.AndCond(identityCondition).AndCond(timeCondition)
	recode = recode.SetCond(finalCondition)

	arrayNum, err := recode.Count()
	totalNum = cast.ToInt(arrayNum)

	_, err = recode.OrderBy("-created_time").Limit(num).All(&collectionDetails)

	collectionSize = len(collectionDetails)
	if err != nil {
		return nil, 0, 0, errors.Errorf("query table error: %v", err)
	}
	return collectionDetails, collectionSize, totalNum, nil
}

func (cdd *CollectionDetailDao) DeleteCollectionDetail(collectionDetailId int) error {
	collectionDetail := new(model.CollectionDetail)
	collectionDetail.Id = collectionDetailId
	_, err := cdd.O.Delete(collectionDetail, "id")
	if err != nil {
		return errors.Errorf("delete error: %v", err)
	}
	return nil
}
